package org.csu.softwaremetrics_demo.metric.ck;

import org.csu.softwaremetrics_demo.entity.common.CKMetricSingleClass;
import org.csu.softwaremetrics_demo.entity.result.Result;
import org.csu.softwaremetrics_demo.metric.Metric;
import org.csu.softwaremetrics_demo.util.ColorLoggerUtils;
import org.eclipse.jdt.core.dom.*;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * CBO (Coupling Between Objects) 类
 * 用于计算类之间的耦合度，即一个类直接依赖的其他类的数量。
 *
 * 耦合可能来自以下几个方面：
 * 1. 属性声明
 * 2. 方法参数和返回类型
 * 3. 局部变量
 * 4. 继承和接口实现
 * 5. 注解使用
 * 6. 异常处理
 *
 * 注意：此实现不是线程安全的。如果需要在多线程环境中使用，
 * 请确保每个线程使用独立的CBO实例。
 *
 * @author Jukomu
 * @version 2.0
 */
public class CBO extends ASTVisitor implements Metric<CKMetricSingleClass> {
    private static final ColorLoggerUtils LOGGER = ColorLoggerUtils.getLogger(CBO.class.getName());

    /**
     * Java标准库的包前缀
     */
    private static final Set<String> JAVA_PACKAGE_PREFIXES;
    static {
        Set<String> prefixes = new HashSet<>();
        prefixes.add("java.");
        prefixes.add("javax.");
        JAVA_PACKAGE_PREFIXES = Collections.unmodifiableSet(prefixes);
    }

    /**
     * 存储当前类所有的耦合类名
     * 使用Set确保每个耦合的类只被计数一次
     */
    private final Set<String> coupling;

    /**
     * 构造函数
     */
    public CBO() {
        this.coupling = new HashSet<>();
    }

    /**
     * 重写visit方法，用于访问VariableDeclarationStatement节点
     *
     * @param node 访问的VariableDeclarationStatement节点
     * @return 访问的结果，返回true表示继续访问子节点，返回false表示停止访问子节点
     */
    @Override
    public boolean visit(VariableDeclarationStatement node) {
        processTypeBinding(node.getType().resolveBinding());
        return super.visit(node);
    }

    /**
     * 重写 visit 方法，用于处理 ClassInstanceCreation 类型的节点。
     *
     * @param node ClassInstanceCreation 类型的节点
     * @return 返回一个布尔值，表示是否继续访问子节点
     */
    @Override
    public boolean visit(ClassInstanceCreation node) {
        processTypeBinding(node.getType().resolveBinding());
        return super.visit(node);
    }

    /**
     * 重写visit方法，用于访问数组创建节点。
     *
     * @param node 数组创建节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(ArrayCreation node) {
        processTypeBinding(node.getType().resolveBinding());
        return super.visit(node);
    }

    /**
     * 覆盖方法，用于访问字段声明节点。
     *
     * @param node 字段声明节点
     * @return 如果当前节点应该被继续访问，则返回true；否则返回false
     */
    @Override
    public boolean visit(FieldDeclaration node) {
        processTypeBinding(node.getType().resolveBinding());
        return super.visit(node);
    }

    /**
     * 重写visit方法，用于访问ReturnStatement节点。
     *
     * @param node 待访问的ReturnStatement节点
     * @return 如果继续访问子节点，则返回true；否则返回false
     */
    @Override
    public boolean visit(ReturnStatement node) {
        if (node != null && node.getExpression() != null) {
            processTypeBinding(node.getExpression().resolveTypeBinding());
        }
        return super.visit(node);
    }

    /**
     * 访问TypeLiteral节点。
     *
     * @param node 要访问的TypeLiteral节点
     * @return 返回是否继续访问子节点
     */
    @Override
    public boolean visit(TypeLiteral node) {
        if (node != null) {
            processTypeBinding(node.resolveTypeBinding());
            processTypeBinding(node.getType().resolveBinding());
        }
        return super.visit(node);
    }

    /**
     * 覆盖visit方法，处理ThrowStatement节点。
     *
     * @param node ThrowStatement节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(ThrowStatement node) {
        if (node != null && node.getExpression() != null) {
            processTypeBinding(node.getExpression().resolveTypeBinding());
        }
        return super.visit(node);
    }

    /**
     * 访问类型声明节点
     *
     * @param node 类型声明节点
     * @return 访问是否成功
     */
    @Override
    public boolean visit(TypeDeclaration node) {
        if (node == null) {
            return false;
        }

        ITypeBinding type = node.resolveBinding();
        if (type == null) {
            LOGGER.warn("Could not resolve binding for type declaration");
            return false;
        }

        // 处理父类
        ITypeBinding superclass = type.getSuperclass();
        if (superclass != null) {
            processTypeBinding(superclass);
        }

        // 处理接口
        for (ITypeBinding interfaceBinding : type.getInterfaces()) {
            processTypeBinding(interfaceBinding);
        }

        return super.visit(node);
    }

    /**
     * 访问方法声明节点。
     *
     * @param node 方法声明节点
     * @return 如果成功访问，则返回true；否则返回false
     */
    @Override
    public boolean visit(MethodDeclaration node) {
        if (node == null) {
            return false;
        }

        IMethodBinding method = node.resolveBinding();
        if (method == null) {
            LOGGER.warn("Could not resolve binding for method declaration");
            return super.visit(node);
        }

        // 处理返回类型
        processTypeBinding(method.getReturnType());

        // 处理参数类型
        for (ITypeBinding param : method.getParameterTypes()) {
            processTypeBinding(param);
        }

        return super.visit(node);
    }

    /**
     * 重写visit方法，用于处理类型转换表达式节点。
     *
     * @param node 待访问的类型转换表达式节点
     * @return 返回布尔值，表示是否继续访问子节点。调用super.visit(node)返回true，否则返回false。
     */
    @Override
    public boolean visit(CastExpression node) {
        if (node != null && node.getType() != null) {
            processTypeBinding(node.getType().resolveBinding());
        }
        return super.visit(node);
    }

    /**
     * 重写 visit 方法，用于处理 InstanceofExpression 类型的节点。
     *
     * @param node InstanceofExpression 类型的节点
     * @return 返回一个布尔值，表示是否继续遍历其他节点
     */
    @Override
    public boolean visit(InstanceofExpression node) {
        if (node != null) {
            processTypeBinding(node.getRightOperand().resolveBinding());
            processTypeBinding(node.getLeftOperand().resolveTypeBinding());
        }
        return super.visit(node);
    }

    /**
     * 重写visit方法，处理NormalAnnotation节点。
     *
     * @param node NormalAnnotation节点
     * @return 布尔值，指示是否继续访问子节点
     */
    @Override
    public boolean visit(NormalAnnotation node) {
        processTypeBinding(node.resolveTypeBinding());
        return super.visit(node);
    }

    /**
     * 重写visit方法，用于处理MarkerAnnotation类型的节点。
     *
     * @param node MarkerAnnotation节点对象
     * @return 调用父类的visit方法并返回其结果
     */
    @Override
    public boolean visit(MarkerAnnotation node) {
        processTypeBinding(node.resolveTypeBinding());
        return super.visit(node);
    }

    /**
     * 处理单成员注解节点。
     *
     * @param node 单成员注解节点
     * @return 是否继续访问
     */
    @Override
    public boolean visit(SingleMemberAnnotation node) {
        // 处理类型绑定
        processTypeBinding(node.resolveTypeBinding());
        // 继续访问
        return super.visit(node);
    }

    /**
     * 访问ParameterizedType节点并处理其类型绑定和泛型类型参数。
     *
     * @param node ParameterizedType节点，表示泛型类型。
     * @return 如果继续访问子节点则返回true，否则返回false。
     */
    @Override
    public boolean visit(ParameterizedType node) {
        if (node == null) {
            return false;
        }

        // 解析ParameterizedType节点的类型绑定
        ITypeBinding binding = node.resolveBinding();
        if (binding == null) {
            // 如果无法解析类型绑定，则记录警告并调用父类的visit方法
            LOGGER.warn("Could not resolve binding for parameterized type");
            return super.visit(node);
        }

        // 处理类型绑定
        processTypeBinding(binding);

        // 处理泛型类型参数
        for (ITypeBinding typeArg : binding.getTypeArguments()) {
            // 递归处理每个泛型类型参数
            processTypeBinding(typeArg);
        }

        // 调用父类的visit方法以继续访问子节点
        return super.visit(node);
    }

    /**
     * 处理类型绑定，将非Java原生类型添加到耦合集合中
     * 此方法会检查并处理类型的有效性，包括：
     * 1. 空值检查
     * 2. 通配符类型检查
     * 3. 原始类型检查
     * 4. Java标准库类型检查
     *
     * @param binding 类型绑定对象
     */
    private void processTypeBinding(ITypeBinding binding) {
        try {
            if (!isValidBinding(binding)) {
                return;
            }

            String qualifiedName = binding.getQualifiedName();
            if (qualifiedName == null || qualifiedName.equals("null")) {
                return;
            }

            if (!isJavaStandardType(qualifiedName) && !binding.isPrimitive()) {
                coupling.add(qualifiedName.replace("[]", ""));
            }
        } catch (Exception e) {
            LOGGER.warn("Error processing type binding: " + e.getMessage());
        }
    }

    /**
     * 检查类型绑定是否有效
     *
     * @param binding 类型绑定对象
     * @return 如果绑定有效返回true，否则返回false
     */
    private boolean isValidBinding(ITypeBinding binding) {
        return binding != null && !binding.isWildcardType();
    }

    /**
     * 判断类型是否来自Java标准库
     *
     * @param qualifiedName 完全限定的类型名
     * @return 如果类型来自java或javax包则返回true
     */
    private boolean isJavaStandardType(String qualifiedName) {
        if (qualifiedName == null) {
            return false;
        }
        return JAVA_PACKAGE_PREFIXES.stream()
                .anyMatch(qualifiedName::startsWith);
    }

    /**
     * 执行CBO（Coupling Between Objects）度量计算。
     *
     * @param cu the CompilationUnit to be analyzed, 编译单元，待分析的代码单元
     * @param singleMetric the CKMetricSingleClass instance to store the metric results, CKMetricSingleClass实例，用于存储度量结果
     * @param result the Result instance to store the overall results, Result实例，用于存储总体结果
     */
    @Override
    public void execute(CompilationUnit cu, CKMetricSingleClass singleMetric, Result result) {
        // 检查输入参数是否为null
        if (cu == null || singleMetric == null) {
            // 如果 CompilationUnit 或 CKMetricSingleClass 为null，记录错误日志并返回
            LOGGER.error("Cannot execute CBO metric calculation with null parameters");
            return;
        }
        try {
            // 使用当前对象（this）作为访问者（Visitor）来访问 CompilationUnit
            cu.accept(this);
        } catch (Exception e) {
            // 如果在计算CBO度量时发生异常，记录错误日志并标记度量计算出错
            LOGGER.error("Error during CBO metric calculation: " + e.getMessage());
            singleMetric.error();
        }
    }

    /**
     * 设置单个类的度量指标。
     *
     * @param singleMetric 单个类的度量指标对象
     * 此方法用于设置传入的CKMetricSingleClass对象的CBO（类耦合度）指标。
     * 如果传入的对象为非空，则计算并设置其CBO值；如果对象为空，则记录错误日志。
     */
    @Override
    public void setSingleMetric(CKMetricSingleClass singleMetric) {
        // 检查传入的singleMetric对象是否为非空
        if (singleMetric != null) {
            // 如果singleMetric对象不为空，则设置其CBO（类耦合度）指标
            // coupling.size() 返回当前类与其他类的耦合度数量
            singleMetric.setCbo(coupling.size());
        } else {
            // 如果singleMetric对象为空，则记录错误日志
            LOGGER.error("Cannot set CBO metric on null object");
        }
    }

    /**
     * 获取当前累计的耦合数量
     * 主要用于测试目的
     *
     * @return 耦合类的数量
     */
    protected int getCouplingCount() {
        return coupling.size();
    }

    /**
     * 获取当前累计的耦合类集合的不可修改视图
     * 主要用于测试目的
     *
     * @return 耦合类集合的不可修改视图
     */
    protected Set<String> getCouplingClasses() {
        return Collections.unmodifiableSet(coupling);
    }
}